/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package shaman.storage.impl.filter.transposition;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import shaman.storage.impl.stream.filter.block.BlockTransformer;

/**
 *
 * @author dmitriy
 */
public class Permutation implements BlockTransformer, Cloneable {

    private final int blockSize;
    private final int[] transpositions;

    public Permutation(int blockSize, int[] permutationSpec)
    {
        this.blockSize=blockSize;
        if (blockSize<=0)
            throw new IllegalArgumentException("Block size must be positive");
        if (permutationSpec.length!=blockSize)
            throw new IllegalArgumentException("Permutation size doesn't match block size");
        this.transpositions=build(blockSize, permutationSpec);
    }

    public int getBlockSize() {
        return blockSize;
    }

    public void forward(byte[] data) {
        if (data.length!=blockSize)
            throw new IllegalArgumentException("Data size doesn't match block size");
        int i;
        for (i=0; i<transpositions.length; i+=2)
        {
            int pos1=transpositions[i];
            int pos2=transpositions[i+1];
            byte t=data[pos1];
            data[pos1]=data[pos2];
            data[pos2]=t;
        }
    }

    public void reverse(byte[] data) {
        int i;
        for (i=transpositions.length-2; i>=0; i-=2)
        {
            int pos1=transpositions[i];
            int pos2=transpositions[i+1];
            byte t=data[pos1];
            data[pos1]=data[pos2];
            data[pos2]=t;
        }
    }

    private static int[] build(int blockSize, int[] permutationSpec)
    {
        int[] positions=new int[blockSize];
        int[] permutation=new int[blockSize];
        int i;
        for (i=0; i<blockSize; i++)
        {
            positions[i]=i;
            permutation[i]=i;
        }
        List<int[]> transp=new LinkedList<int[]>();
        for (i=0; i<blockSize; i++)
        {
            int pos1=i;
            int pos2=permutationSpec[i];
            if (pos2<0 || pos2>=blockSize)
                throw new IllegalArgumentException("Invalid value in permutation specification");
            pos2=positions[pos2];
            transp.add(new int[]{pos1, pos2});
            int el1=permutation[i];
            int el2=permutationSpec[i];
            int t=positions[el1];
            positions[el1]=positions[el2];
            positions[el2]=t;
            t=permutation[pos1];
            permutation[pos1]=permutation[pos2];
            permutation[pos2]=t;
        }
        int length=transp.size();
        int[] ret=new int[length*2];
        Iterator<int[]> it=transp.iterator();
        for (i=0; i<ret.length; i+=2)
        {
            int[] curTransp=it.next();
            ret[i]=curTransp[0];
            ret[i+1]=curTransp[1];
        }
        return ret;
    }
}
